package com.xcity.game.activity.redpacket;

import java.text.MessageFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import com.xcity.Server;
import com.xcity.db.entity.Config;
import com.xcity.db.entity.Player;
import com.xcity.db.mapper.ConfigMapper;
import com.xcity.game.activity.ActivityService;
import com.xcity.game.activity2.ActivityIds;
import com.xcity.game.chat.ChatService;
import com.xcity.game.common.Cause;
import com.xcity.game.common.GameStaticConfig;
import com.xcity.game.common.MessageConst;
import com.xcity.game.common.PlayerPool;
import com.xcity.game.event.GameEvents;
import com.xcity.game.player.PlayerService;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.NoticePush;
import com.xcity.pomelo.push.msg.ActivityStatePush;
import com.xcity.pomelo.request.ActivityRedPacketRequest;
import com.xcity.pomelo.response.ActivityRedPacketResponse;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.util.BackDoorHandler;
import com.xcity.util.DBUtils;
import com.xcity.util.SessionUtils;

import gnu.trove.list.TLongList;
import gnu.trove.list.array.TLongArrayList;
import naga.x.App;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.game.time.DayListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Packet;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.ServiceException;
import naga.x.util.CommonUtils;
import naga.x.util.TimeUtils;

/**
 * <pre>
 * 粽子红包
 * 活动时间范围：2017-05-29 00:00:00~2017-05-31 23:59:59
 * 每天发放红包时间：12:00:00, 18:00:00, 21:00:00
 * 红包持续时间：30分钟
 * 红包发放公告：提前3分钟，每分钟1次（即前3、2、1分钟分别公告一次）
 * 活动公告时间：09:00:00, 10:00:00, 11:00:00, 13:00:00, 14:00:00, 15:00:00, 16:00:00, 17:00:00, 19:00:00, 20:00:00
 * 红包总额：Integer.MAX_VALUE
 * 单个红包金额：50万~100万
 * </pre>
 * @author yang.li
 *
 */
@OPHandler
public class RedPacketActivityService extends ActivityService implements EventListener, DayListener {

	protected byte state = STATE_CLOSED;
	// 活动时间范围
	protected long beginTime = TimeUtils.parse("2017-05-29 00:00:00").getTime();
	protected long endTime = TimeUtils.parse("2017-05-31 23:59:59").getTime();
	// 最近一次发红包时间
	protected long lastSendTime;
	// 红包剩余金额
	protected AtomicInteger pool;
	// 单个红包金额范围
	protected int minVal = 500000, maxVal = 1000000;
	// 提前3分钟红包通知
	protected int packetNoticeShiftTime = 3 * 60000;
	// 红包持续时间（30分钟）
	protected int packetDuration = 30 * 60000;
	
	public static boolean DEBUG = BackDoorHandler.CHEAT_ENABLE;
	
	class ChangeStateTask implements Runnable {
		byte state; // try change to the state
		ChangeStateTask(byte state) {
			this.state = state;
		}
		@Override
		public void run() {
			if (state == STATE_OPENED && pool.get() > 0) {
				RedPacketActivityService.this.state = STATE_OPENED;
				lastSendTime = TimeUpdater.getInstance().now();
				Collection<Player> players = App.getApp().getServiceManager().get(PlayerService.class).getOnlinePlayers();
				Packet packet = new ActivityStatePush(ActivityIds.ACTIVITY_RED_PACKET, STATE_OPENED);
				for (Player player : players) {
					if (isCanGetRedPacket(player)) {
						player.send(packet);
					}
				}
			} else {
				RedPacketActivityService.this.state = STATE_CLOSED;
				Server.broadcast(new ActivityStatePush(ActivityIds.ACTIVITY_RED_PACKET, STATE_CLOSED));
			}
		}
	}
	
	class NoticeTask implements Runnable {
		String message;
		NoticeTask(String message) {
			this.message = message;
		}
		@Override
		public void run() {
			if (pool.get() > 0) {
				Server.broadcast(new NoticePush(NoticePush.NOTICE_MARQUEE, message));
				App.getApp().getServiceManager().get(ChatService.class).sendSystemChat(message);
			}
		}
	}
	
	@Override
	public void startup() throws ServiceException {
		TimeUpdater.getInstance().update(); // try force update, ignore delay
		if (TimeUpdater.getInstance().now() >= endTime) {
			return;
		}
		Config config = DBUtils.find(Config.KEY_RED_PACKET, ConfigMapper.class);
		if (config == null) {
			config = new Config(Config.KEY_RED_PACKET, Integer.MAX_VALUE);
			DBUtils.save(config, ConfigMapper.class);
		}
		pool = new AtomicInteger(config.getIntValue());
		TimeUpdater.getInstance().update(); // try force update, ignore delay
		if (DEBUG) {
			makeScheduleForDebug();
		} else {
			makeSchedule();
		}
		App.getApp().getEventManager().register(this);
		TimeUpdater.getInstance().addDayListener(this);
	}
	
	private void makeScheduleForDebug() {
		beginTime = TimeUpdater.getInstance().now();
		endTime = Long.MAX_VALUE;
		packetDuration = 5 * 60000;
		long t = TimeUtils.getTodayZeroTime();
		TLongList times = new TLongArrayList();
		Calendar calendar = Calendar.getInstance();
		for (;;) { // 测试  每10分钟一轮
			calendar.setTimeInMillis(t);
			int day = (calendar.get(Calendar.YEAR) << 16) | calendar.get(Calendar.DAY_OF_YEAR);
			if (day != TimeUpdater.getInstance().today()) {
				break;
			}
			times.add(t);
			t += TimeUnit.MINUTES.toMillis(10);
		}
		long[] sendPacketTimes = times.toArray();
		
		times.clear();
		t = TimeUtils.getTodayZeroTime();
		for (;;) { // 测试  活动公告每20分钟一轮
			calendar.setTimeInMillis(t);
			int day = (calendar.get(Calendar.YEAR) << 16) | calendar.get(Calendar.DAY_OF_YEAR);
			if (day != TimeUpdater.getInstance().today()) {
				break;
			}
			times.add(t);
			t += TimeUnit.MINUTES.toMillis(20);
		}
		long[] activityNoticeTimes = times.toArray();
		
		makeSchedule(sendPacketTimes, activityNoticeTimes);
	}
	
	public void makeSchedule() {
//		12:00:00, 18:00:00, 21:00:00
		long t = TimeUtils.getTodayZeroTime();
		long[] sendPacketTimes = null;
		if (isValid()) { // 活动时间范围内才计划执行
			sendPacketTimes = new long[] {
					t + TimeUnit.HOURS.toMillis(12),
					t + TimeUnit.HOURS.toMillis(18),
					t + TimeUnit.HOURS.toMillis(21),
			};
		}
//		09:00:00, 10:00:00, 11:00:00, 13:00:00, 14:00:00, 15:00:00, 16:00:00, 17:00:00, 19:00:00, 20:00:00
		long[] activityNoticeTimes = {
				t + TimeUnit.HOURS.toMillis(9),
				t + TimeUnit.HOURS.toMillis(10),
				t + TimeUnit.HOURS.toMillis(11),
				t + TimeUnit.HOURS.toMillis(13),
				t + TimeUnit.HOURS.toMillis(14),
				t + TimeUnit.HOURS.toMillis(15),
				t + TimeUnit.HOURS.toMillis(16),
				t + TimeUnit.HOURS.toMillis(17),
				t + TimeUnit.HOURS.toMillis(19),
				t + TimeUnit.HOURS.toMillis(20),
		};
		makeSchedule(sendPacketTimes, activityNoticeTimes);
	}
	
	static String formatMillisToHours(long time) {
		if (DEBUG) {
			return TimeUtils.format("HH:mm", time);
		}
		return TimeUtils.format("HH点", time);
	}
	
	public void makeSchedule(long[] sendPacketTimes, long[] activityNoticeTimes) {
		for (int i = 0, len = sendPacketTimes != null ? sendPacketTimes.length : 0; i < len; i++) { // 活动未开启前sendPacketTimes为null
			long time = sendPacketTimes[i];
			long delay;
			// 发红包通知（提前3分钟，每分钟1次）
			long shift = packetNoticeShiftTime;
			while (shift > 0) {
				delay = time - shift - TimeUpdater.getInstance().now();
				if (delay >= 0) {
					String msg = MessageFormat.format(MessageConst.ACTIVITY_RED_PACKET_SEND_NOTICE, formatMillisToHours(time), TimeUnit.MILLISECONDS.toMinutes(shift));
					App.getApp().getSchedule().schedule(new NoticeTask(msg), delay, TimeUnit.MILLISECONDS);
				}
				shift -= TimeUnit.MINUTES.toMillis(1);
			}
			// 发红包
			delay = time - TimeUpdater.getInstance().now();
			if (delay >= 0) {
				App.getApp().getSchedule().schedule(new ChangeStateTask(STATE_OPENED), delay, TimeUnit.MILLISECONDS);
			}
			// 关闭发放红包
			delay = time + packetDuration - TimeUpdater.getInstance().now();
			if (delay >= 0) {
				App.getApp().getSchedule().schedule(new ChangeStateTask(STATE_CLOSED), delay, TimeUnit.MILLISECONDS);
			}
		}
		
		// 活动公告
		for (long time : activityNoticeTimes) {
			long delay = time - TimeUpdater.getInstance().now();
			if (delay >= 0) {
				App.getApp().getSchedule().schedule(new NoticeTask(MessageConst.ACTIVITY_RED_PACKET_NOTICE), delay, TimeUnit.MILLISECONDS);
			}
		}
	}
	
	@Override
	public void shutdown() throws ServiceException {
		if (pool != null) {
			DBUtils.update(new Config(Config.KEY_RED_PACKET, pool.get()), ConfigMapper.class);
		}
	}
	
	@Override
	public int[] getEventTypes() {
		return new int[] {
				GameEvents.EVENT_PLAYER_LOGIN,
				GameEvents.EVENT_PLAYER_LOADED,
				GameEvents.EVENT_DAILY_REWARD_GOT,
				GameEvents.EVENT_FRESHER_GUIDE_END,
		};
	}

	@Override
	public void handleEvent(Event event) {
		Player player = event.getParameter();
		if (event.getType() == GameEvents.EVENT_PLAYER_LOGIN) {
			if (player.getPool().getShortValue(PlayerPool.PROPERTY_GUIDE) < GameStaticConfig.fresherMaxGuide) {
				// 登录时检测下是否未过新手引导
				player.getSession().setAttribute(SessionUtils.FRESHER_WHEN_LOGIN, true);
			}
		} else {
			if (isCanGetRedPacket(player)) {
				player.send(new ActivityStatePush(ActivityIds.ACTIVITY_RED_PACKET, state));
			}
		}
	}
	
	@Override
	public boolean dayChanged() {
		if (TimeUpdater.getInstance().now() >= endTime) {
			return true;
		}
		if (DEBUG) {
			makeScheduleForDebug();
		} else {
			makeSchedule();
		}
		return true;
	}

	@PomeloOP(PomeloRoute.ACTIVITY_RED_PACKET_GET)
	protected void requestOpenRedPacket(Session session, ActivityRedPacketRequest req) {
		if (!isEnable()) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ACTIVITY_NOT_BEGIN));
			return;
		}
		Player player = session.getClient();
		long lastGetRedPacketTime = player.getPool().getLongValue(PlayerPool.PROPERTY_LAST_GET_RED_PACKET_TIME);
		if (lastGetRedPacketTime >= lastSendTime) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ACTIVITY_RED_PACKET_GET_REPEATLY));
			return;
		}
		int remainderVal = pool.get();
		if (remainderVal <= 0) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ACTIVITY_RED_PACKET_OVER));
			return;
		}
		int val = CommonUtils.randomInt(minVal, maxVal);
		if (val > remainderVal) {
			val = remainderVal;
		}
		pool.getAndAdd(-val);
		player.poolSet(PlayerPool.PROPERTY_LAST_GET_RED_PACKET_TIME, TimeUpdater.getInstance().now());
		player.addGold(val, Cause.RED_PACKET_ACTIVITY);
		session.send(new ActivityRedPacketResponse(req.getSerial(), val));
		session.send(new ActivityStatePush(ActivityIds.ACTIVITY_RED_PACKET, STATE_CLOSED));
	}

	public boolean isValid() {
		return isValid(TimeUpdater.getInstance().now());
	}
	
	public boolean isValid(long time) {
		return time >= beginTime && time <= endTime;
	}
	
	public boolean isEnable() {
		return state == STATE_OPENED;
	}
	
	public boolean isCanGetRedPacket(Player player) {
		return isEnable()
//				&& player.getPool().getShortValue(PlayerPool.PROPERTY_GUIDE) >= GameStaticConfig.fresherMaxGuide // 已过新手引导
				&& player.getLevel() > 3
				&& player.getPool().getLongValue(PlayerPool.PROPERTY_LAST_GET_RED_PACKET_TIME) < lastSendTime // 未领取过红包
				/*&& (
						// 已领取过每日登录奖励
						!DailyLoginActivityService.isFirstLoginToday(player)
						// 刚过新手引导，但未领取每日登录奖励
						|| (player.getSession() != null && player.getSession().getAttribute(SessionUtils.FRESHER_WHEN_LOGIN) != null)
					)*/;
	}
	
}
